from django import http
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View
from django.contrib.auth import login, authenticate, logout
from django_redis import get_redis_connection
from django.conf import settings
from django.contrib.auth import mixins
from celery_tasks.email.tasks import send_emails
from django.db import DatabaseError
import re
import json

from meiduo_mall.utils.response_code import RETCODE
from .models import User
from meiduo_mall.utils.login_user import LoginUserView
from .utils import generate_email_url,check_email_url
from .models import Address
from goods.models import SKU
from carts.utils import merge_carts_cookie_to_redis
from orders.models import OrderInfo,OrderGoods
import logging
logger = logging.getLogger()


class RegisterView(View):
    """注册视图"""

    def get(self, request):
        return render(request, 'register.html')

    # 处理注册请求
    def post(self, request):

        query_dict = request.POST  # 接收表单数据
        username = query_dict.get('username')
        password = query_dict.get('password')
        password2 = query_dict.get('password2')
        mobile = query_dict.get('mobile')
        sms_code_client = query_dict.get('sms_code')
        allow = query_dict.get('allow')  # 勾选前端无指定value为on，不勾选为None
        # 校验参数是否齐全
        if not all([username, password, mobile, sms_code_client, allow]):
            return http.HttpResponseForbidden('参数不全')
        # 校验用户名格式
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return http.HttpResponseForbidden('请输入5-20个字符的用户名')
        # 校验密码格式
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.HttpResponseForbidden('请输入8-20个字符的密码')
        # 校验两次密码是否一致
        if password != password2:
            return http.HttpResponseForbidden('两次密码不一致')
        # 校验手机号格式
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('您输入的手机格式不正确')
        # 校验手机验证码
        redis_conn = get_redis_connection('verify_code')
        sms_code_server = redis_conn.get('sms_code_%s' % mobile)
        redis_conn.delete('sms_code_%s' % mobile)
        if sms_code_server is None:
            return http.HttpResponseForbidden('短信验证码已过期')
        if sms_code_client != sms_code_server.decode():
            return http.HttpResponseForbidden('短信验证码输入错误')
        # 存储用户数据
        user = User.objects.create_user(username=username, password=password, mobile=mobile)
        # 保持用户登录状态
        login(request, user)
        return redirect(reverse('contents:index'))


class UsernameCountView(View):
    """用户名重复校验"""

    def get(self, request, username):
        count = User.objects.filter(username=username).count()
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count})


class MobileCountView(View):
    """手机重复校验"""

    def get(self, request, mobile):
        count = User.objects.filter(mobile=mobile).count()
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count})


class LoginView(View):
    """登录视图"""

    def get(self, request):
        """登录界面"""
        return render(request, 'login.html')

    def post(self, request):
        # 接收请求表单（username,password,remembered)
        username = request.POST.get('username')
        password = request.POST.get('password')
        remembered = request.POST.get('remembered')
        # 校验
        user = authenticate(request, username=username, password=password)
        # 判断用户是否存在
        if user is None:
            return render(request, 'login.html', {'account_errmsg': '账号密码输入错误'})
        login(request, user)
        # 判断是否勾选记住登录
        if remembered != 'on':
            request.session.set_expiry(0)

        # 登录用户有可能来自用户中心等来源，需要重定向回原处
        # 来自用户中心的请求地址:/login/?next=info,提取next放or前面先进行判断

        # response = redirect(request.GET.get('next') or reverse('contents:index'))
        response = redirect(request.GET.get('next') or reverse('contents:index'))
        # 设置cookie,前端需要将首页登录状态改为用户登录状态
        # response.set_cookie('username',user.username,max_age=(60*60*24*7*2) if remembered else None)
        response.set_cookie('username', user.username, max_age=settings.SESSION_COOKIE_AGE if remembered else None)
        # 重定向响应
        # 合并购物车
        merge_carts_cookie_to_redis(request, response)
        return response


class LogoutView(View):
    """退出登录视图"""

    def get(self, request):
        # 退出登录一般是用户要切换账号，所以重定向到登录界面,还要清除状态保持和删掉登录设置的cookie
        logout(request)
        response = redirect(reverse('users:login'))
        response.delete_cookie('username')
        return response


#
# class UserInfoView(View):
#     """用户中心视图"""
#     def get(self,request):
#         # 登录用户中心之前要先判断是否登录，未登录重定向到登录页面
#         if isinstance(request.user,User):
#             return render(request, 'user_center_info.html')
#         else:
#             return redirect('/login/?next=/info/')


class UserInfoView(mixins.LoginRequiredMixin, View):
    """用户中心视图"""

    def get(self, request):
        return render(request, 'user_center_info.html')


class EmailView(LoginUserView):
    """设置邮箱"""
    # 前端为put请求，请求地址：url = this.host + '/emails/';
    def put(self, request):
        query_dict = request.body.decode()

        json_dict = json.loads(query_dict)
        email = json_dict.get('email')
        if not email:
            return http.JsonResponse({'code':RETCODE.NECESSARYPARAMERR, 'errmsg':'缺少email参数'})
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return http.HttpResponseForbidden('邮箱格式错误')
        # 获取对象
        user = request.user
        # 保存邮箱
        # user.email = email  # 这种写法会在重新发送邮件时重复设置邮箱
        # user.save()
        # 一旦邮箱设置成功就不能再修改
        User.objects.filter(id=user.id, email='').update(email=email)
        # 发送邮件
        # 发邮件不需要等待结果可添加到celery异步处理
        # from django.core.mail import send_mail
        # send_mail(subject='邮件主题',
        #           message='邮件普通正文',
        #           from_email=settings.EMAIL_FROM, # 发件人
        #           recipient_list=[email], # 收件人列表
        #           html_message=None, # 超文本文件内容
        #           fail_silently=False, auth_user=None, auth_password=None,
        #           connection=None)

        # 生成一个加密链接通过邮件发给用户认证,用户点击发送请求，通过提取加密内容判断修改邮箱激活状态
        # http://www.meiduo.site:8000/emails/verifications/?token=
        verify_url = generate_email_url(user)
        send_emails.delay(email,verify_url)

        return http.JsonResponse({'code':RETCODE.OK,'errmsg':'OK'})


class CheckEmailView(View):
    """邮箱验证"""
    def get(self, request):

        # 接收数据
        token = request.GET.get('token')
        # 校验
        if token is None:
            return http.HttpResponseForbidden('所传参数不全')
        user = check_email_url(token)
        if user is None:
            return http.HttpResponseForbidden('无效的token')
        # 逻辑处理

        user.email_active = True
        user.save()
        # 验证成功，重定向回个人中心
        return redirect(reverse('users:info'))


class AddressesView(LoginUserView):
    """显示收获地址"""
    def get(self, request):
        user = request.user
        # 用户所有收获地址查询集
        address_qs = Address.objects.filter(user=user, is_deleted=False)
        # 定义一个空列表装用户所有未删收获地址字典[{}{}]
        address_list = []
        for address_model in address_qs:
            address_dict = {
                'id': address_model.id,
                'title': address_model.title,
                'receiver': address_model.receiver,
                'province_id': address_model.province_id,
                'city_id': address_model.city_id,
                'district_id': address_model.district_id,
                'place': address_model.place,
                'mobile': address_model.mobile,
                'tel': address_model.tel,
                'email': address_model.email,
            }
            address_list.append(address_dict)
        # 返回内容 {'addresses':用户所有收获地址列表, 'default_address_id'：默认收获地址id}
        context = {
            'addresses': address_list,
            'default_address_id': user.default_address_id

        }
        return render(request, 'user_center_site.html',context)


class CreateAddressesView(LoginUserView):
    """新增收获地址"""
    def post(self, request):
        # 先判断用户收获地址是否超过20个
        user = request.user
        count = Address.objects.filter(user=user,is_deleted=False).count()
        if count >= 20:
            return http.JsonResponse({'code':RETCODE.Maxnum, 'errmsg':'超出数量'})
        # 接收非表单数据
        query_dict = request.body.decode()
        json_dict = json.loads(query_dict)
        title = json_dict.get('title')
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')
        # 校验
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')
        try:
            address = Address.objects.create(
                user=user,
                title=title,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except DatabaseError as e:
            logger.error(e)
            return http.HttpResponseForbidden('收获地址创建失败')
        # 判断是否有默认收获地址
        if user.default_address is None:
            user.default_address = address
            user.save()
        # 准备一个大字典返回address：{}
        data_dict = {
            'id':address.id,
            'title':title,
            'receiver':receiver,
            'province_id':province_id,
            'city_id':city_id,
            'district_id':district_id,
            'place':place,
            'mobile':mobile,
            'tel':tel,
            'email':email,
        }
        return http.JsonResponse({'code':RETCODE.OK, 'errmsg':'添加收货地址成功','address':data_dict})


class ChangeDeleteAddressView(LoginUserView):
    """修改和删除收获地址"""
    def put(self, request, address_id):

        user = request.user
        # 判断用户是否为正常用户
        try:
            address = Address.objects.get(id=address_id, user=user, is_deleted=False)
        except Address.DoesNotExist:
            return http.HttpResponseForbidden('修改收获地址失败')
        # 接收非表单数据
        query_dict = request.body.decode()
        json_dict = json.loads(query_dict)
        title = json_dict.get('title')
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')
        # 校验
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')
        try:
                address.user=user
                address.title=title
                address.receiver=receiver
                address.province_id=province_id
                address.city_id=city_id
                address.district_id=district_id
                address.place=place
                address.mobile=mobile
                address.tel=tel
                address.email=email
                address.save()
        except DatabaseError as e:
            logger.error(e)
            return http.HttpResponseForbidden('收获地址修改失败')
        # 准备一个大字典返回address：{}
        data_dict = {
            'id': address.id,
            'title': title,
            'receiver': receiver,
            'province_id': province_id,
            'city_id': city_id,
            'district_id': district_id,
            'place': place,
            'mobile': mobile,
            'tel': tel,
            'email': email,
        }
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '添加收货地址成功', 'address': data_dict})

    def delete(self,request, address_id):
        # 判断用户是否为正常用户
        user = request.user
        try:
            address = Address.objects.get(id=address_id, user=user, is_deleted=False)
        except Address.DoesNotExist:
            return http.HttpResponseForbidden('删除收获地址失败')
        # 逻辑删除
        address.is_deleted = True
        address.save()
        # 物理删除
        # address.delete()
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '删除收货地址成功'})


class DefaultAddressView(LoginUserView):
    """设置默认收获地址"""
    def put(self, request, address_id):
        # 判断用户是否为正常用户
        user = request.user
        try:
            address = Address.objects.get(id=address_id, user=user, is_deleted=False)
        except Address.DoesNotExist:
            return http.HttpResponseForbidden('设置默认收获地址失败')
        user.default_address = address
        user.save()
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置默认收货地址成功'})


class AddressTitleView(LoginUserView):
    """修改收获地址标题"""
    def put(self, request, address_id):
        # 判断用户是否为正常用户
        user = request.user
        try:
            address = Address.objects.get(id=address_id, user=user, is_deleted=False)
        except Address.DoesNotExist:
            return http.HttpResponseForbidden('设置默认收获地址失败')
        json_dict = json.loads(request.body.decode())
        title = json_dict.get('title')
        address.title =title
        address.save()
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置默认收货地址成功'})


class ChangePasswordView(LoginUserView):
    """修改密码"""
    def get(self, request):
        return render(request, 'user_center_pass.html')

    def post(self, request):
        # 接收表单数据
        old_pwd = request.POST.get('old_pwd')
        new_pwd = request.POST.get('new_pwd')
        new_cpwd = request.POST.get('new_cpwd')
        # 校验
        if not all([old_pwd, new_pwd, new_cpwd]):
            return http.HttpResponseForbidden('缺少必传参数')
        if request.user.check_password(old_pwd) is False:
            return render(request, 'user_center_pass.html', {'change_pwd_errmsg':'原始密码错误'})
        if not re.match(r'^[0-9A-Za-z]{8,20}$', new_pwd):
            return http.HttpResponseForbidden('请输入8-20个字符的密码')
        if new_pwd != new_cpwd:
            return http.HttpResponseForbidden('两次密码输入不一致')
        # 修改加密保存
        request.user.set_password(new_pwd)  # set_password 会清除会话（删除session)
        request.user.save()
        # 响应重定向回登录页面,重定向之前要删除cookie（重定向退出不用）
        return redirect(reverse('users:logout'))


class HistoryGoodsView(View):
    """历史浏览记录"""
    def post(self, request):
        """保存历史浏览记录"""
        # 判断用户是否登录
        user = request.user
        if not user.is_authenticated:
            return http.JsonResponse({'code':RETCODE.SESSIONERR,'errmsg':'用户未登录'})
        # 获取请求体中的sku_id
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        # 判断sku_id是否存在
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku_id不存在')
        # 保存历史浏览记录到redis {hitory_user.id:[sku.id,xx,...]}
        # 创建redis连接对象
        redis_conn = get_redis_connection('history')
        key = 'history_%s'% user.id
        # 创建redis管道对象
        pl = redis_conn.pipeline()
        # 先去重
        pl.lrem(key, 0, sku.id)
        # 把sku.id添加到列表左边
        pl.lpush(key, sku.id)
        # 只保存5个
        pl.ltrim(key, 0, 4)
        pl.execute()
        # 响应
        return http.JsonResponse({'code':RETCODE.OK,'errmsg':'OK'})

    def get(self, request):
        """展示历史浏览记录"""
        # 判断用户是否登录
        user = request.user
        if not user.is_authenticated:
            return http.JsonResponse({'code': RETCODE.SESSIONERR, 'errmsg': '用户未登录'})
        # 连接redis数据库取出数据（lrange(key,0,-1))
        redis_conn = get_redis_connection('history')
        redis_list = redis_conn.lrange('history_%s'%user.id, 0,-1)
        # 准备一个空列表返回
        sku_list = []
        for sku_id in redis_list:
            sku = SKU.objects.get(id=sku_id)
            sku_dict = {
                'id':sku.id,
                'name':sku.name,
                'price':sku.price,
                'default_image_url':sku.default_image.url
            }
            sku_list.append(sku_dict)
        # 响应
        return http.JsonResponse({'code':RETCODE.OK,'errmsg':'OK','skus':sku_list})


class MyOrderView(LoginUserView):
    """我的订单"""
    def get(self,request):
        user = request.user
        order_qs = OrderInfo.objects.filter(user_id=user.id)

        page_orders = []

        for order in order_qs:
            sku_list = []
            order_goods_qs = order.skus.all()
            for order_goods in order_goods_qs:
                sku = SKU.objects.get(id=order_goods.sku_id)
                sku_list.append({
                    'name':sku.name,
                    'price':str(sku.price),
                    'amount':str(order_goods.count * sku.price),
                    'count':int(order_goods.count),
                    'default_image_url':sku.default_image.url
                })

            page_orders.append({
                'create_time':order.create_time,
                'order_id':order.order_id,
                'sku_list':sku_list,
                'total_amount':str(order.total_amount),
                'freight':order.freight,
                'pay_method_name':OrderInfo.PAY_METHOD_CHOICES[(order.pay_method) - 1][1], #
                'status': order.status,
                'status_name':OrderInfo.ORDER_STATUS_CHOICES[(order.status - 1)][1]

            })
            page_orders.reverse()
        return render(request,'user_center_order.html',{'page_orders':page_orders})


